﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using iNet.Tasks;
using iNet.Win32;

namespace iNet.Media.Imaging
{
	/// <summary>
	/// 取得點陣圖色階分布的工作。
	/// </summary>
	public class HistogramRetriever : Task
	{
		#region 欄位
		int[] _BlueHistogram;
		int[] _GreenHistogram;
		int[] _LuminanceHistogram;
		int[] _RedHistogram;
		IBitmapImage _SourceImage;
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 HistogramRetriever 執行個體。
		/// </summary>
		public HistogramRetriever()
		{ }
		/// <summary>
		/// 初始化新的 HistogramRetriever 執行個體。
		/// </summary>
		/// <param name="sourceImage">來源點陣圖。</param>
		public HistogramRetriever(IBitmapImage sourceImage)
		{
			_SourceImage = sourceImage;
		}
		#endregion
		#region BlueHistogram
		/// <summary>
		/// 取得藍色的色階分布。
		/// </summary>
		public int[] BlueHistogram
		{
			get
			{
				return _BlueHistogram;
			}
		}
		#endregion
		#region Execute
		/// <summary>
		/// 執行工作。
		/// </summary>
		/// <param name="version">工作版本。</param>
		/// <returns>True 表示工作執行完成或已取消，False 表示失敗。</returns>
		protected override unsafe bool Execute(TaskVersion version)
		{
			//取得資料
			IBitmapImage sourceImage;
			if (!this.ReadValue<IBitmapImage>(version, ref _SourceImage, out sourceImage))
				return true;

			//取得影像資訊
			int width = sourceImage.PixelWidth;
			int height = sourceImage.PixelHeight;

			//計算色彩數量
			PixelFormat pixelFormat = sourceImage.PixelFormat;
			bool isBgr565 = (pixelFormat == PixelFormats.Bgr565);
			int redColorCount, greenColorCount, blueColorCount, luminanceCount;
			if (pixelFormat.Is8BitsColor())
				luminanceCount = redColorCount = greenColorCount = blueColorCount = 256;
			else if (pixelFormat.Is16BitsColor())
				luminanceCount = redColorCount = greenColorCount = blueColorCount = 65536;
			else if (isBgr565)
			{
				luminanceCount = 32;
				redColorCount = blueColorCount = 32;
				greenColorCount = 64;
			}
			else if (pixelFormat == PixelFormats.Indexed1)
				luminanceCount = redColorCount = greenColorCount = blueColorCount = 2;
			else if (pixelFormat == PixelFormats.Gray2 || pixelFormat == PixelFormats.Indexed2)
				luminanceCount = redColorCount = greenColorCount = blueColorCount = 4;
			else if (pixelFormat == PixelFormats.Gray4 || pixelFormat == PixelFormats.Indexed4)
				luminanceCount = redColorCount = greenColorCount = blueColorCount = 16;
			else
				return false;

			//統計色階分布
			System.Diagnostics.Stopwatch w = new System.Diagnostics.Stopwatch();
			w.Start();

			int[] rHistogram = null, gHistogram = null, bHistogram = null, lHistogram = null;
			int* rHistogram1 = null, gHistogram1 = null, bHistogram1 = null, lHistogram1 = null;
			int* rHistogram2 = null, gHistogram2 = null, bHistogram2 = null, lHistogram2 = null;
			try
			{
				//配置記憶體
				rHistogram1 = (int*)Marshal.AllocHGlobal(redColorCount * 4);
				gHistogram1 = (int*)Marshal.AllocHGlobal(greenColorCount * 4);
				bHistogram1 = (int*)Marshal.AllocHGlobal(blueColorCount * 4);
				lHistogram1 = (int*)Marshal.AllocHGlobal(luminanceCount * 4);
				Kernel32.FillMemory(rHistogram1, redColorCount * 4, 0);
				Kernel32.FillMemory(gHistogram1, greenColorCount * 4, 0);
				Kernel32.FillMemory(bHistogram1, blueColorCount * 4, 0);
				Kernel32.FillMemory(lHistogram1, luminanceCount * 4, 0);

				//統計色階分布
				sourceImage.AccessPixelBuffer((IntPtr pixelBuffer, int pixelWidth, int pixelHeight, int stride, PixelFormat pixelFormat2, BitmapPalette palette, ref bool modified) =>
				{
					//平行處理
					bool isParallel = (Environment.ProcessorCount > 1 && (width * height) >= 2000000);
					int halfHeight = (height / 2);
					ManualResetEventSlim barrier;
					if (isParallel)
					{
						IntPtr pixelBuffer2 = new IntPtr(pixelBuffer.ToInt64() + (stride * halfHeight));
						barrier = new ManualResetEventSlim(false);
						rHistogram2 = (int*)Marshal.AllocHGlobal(redColorCount * 4);
						gHistogram2 = (int*)Marshal.AllocHGlobal(greenColorCount * 4);
						bHistogram2 = (int*)Marshal.AllocHGlobal(blueColorCount * 4);
						lHistogram2 = (int*)Marshal.AllocHGlobal(luminanceCount * 4);
						Kernel32.FillMemory(rHistogram2, redColorCount * 4, 0);
						Kernel32.FillMemory(gHistogram2, greenColorCount * 4, 0);
						Kernel32.FillMemory(bHistogram2, blueColorCount * 4, 0);
						Kernel32.FillMemory(lHistogram2, luminanceCount * 4, 0);
						ThreadPool.QueueUserWorkItem((state) =>
						{
							try
							{
								this.GetHistograms(pixelBuffer2, pixelWidth, (pixelHeight - halfHeight), stride, pixelFormat, palette, rHistogram2, gHistogram2, bHistogram2, lHistogram2);
							}
							finally
							{
								barrier.Set();
							}
						});
					}
					else
						barrier = null;

					//使用目前的執行緒統計
					this.GetHistograms(pixelBuffer, pixelWidth, (isParallel ? halfHeight : pixelHeight), stride, pixelFormat, palette, rHistogram1, gHistogram1, bHistogram1, lHistogram1);

					//合併統計結果
					if (isParallel)
					{
						barrier.Wait();
						if (!isBgr565)
						{
							for (int i = luminanceCount - 1; i >= 0; --i)
							{
								rHistogram1[i] += rHistogram2[i];
								gHistogram1[i] += gHistogram2[i];
								bHistogram1[i] += bHistogram2[i];
								lHistogram1[i] += lHistogram2[i];
							}
						}
						else
						{
							for (int i = luminanceCount - 1; i >= 0; --i)
							{
								rHistogram1[i] += rHistogram2[i];
								bHistogram1[i] += bHistogram2[i];
								lHistogram1[i] += lHistogram2[i];
							}
							for (int i = greenColorCount - 1; i >= 0; --i)
								gHistogram1[i] += gHistogram2[i];
						}
					}
				});

				//中斷確認
				if (this.IsCanceling)
					return true;

				//轉換成陣列
				rHistogram = new int[redColorCount];
				gHistogram = new int[greenColorCount];
				bHistogram = new int[blueColorCount];
				lHistogram = new int[luminanceCount];
				Marshal.Copy(new IntPtr(rHistogram1), rHistogram, 0, redColorCount);
				Marshal.Copy(new IntPtr(gHistogram1), gHistogram, 0, greenColorCount);
				Marshal.Copy(new IntPtr(bHistogram1), bHistogram, 0, blueColorCount);
				Marshal.Copy(new IntPtr(lHistogram1), lHistogram, 0, luminanceCount);
			}
			finally
			{
				//釋放記憶體
				Marshal.FreeHGlobal(new IntPtr(rHistogram1));
				Marshal.FreeHGlobal(new IntPtr(gHistogram1));
				Marshal.FreeHGlobal(new IntPtr(bHistogram1));
				Marshal.FreeHGlobal(new IntPtr(lHistogram1));
				Marshal.FreeHGlobal(new IntPtr(rHistogram2));
				Marshal.FreeHGlobal(new IntPtr(gHistogram2));
				Marshal.FreeHGlobal(new IntPtr(bHistogram2));
				Marshal.FreeHGlobal(new IntPtr(lHistogram2));
			}
			long time = w.ElapsedMilliseconds;

			//中斷確認
			if (this.IsCanceling)
				return true;

			//儲存結果
			this.WriteValue<int[]>(version, ref _LuminanceHistogram, lHistogram);
			this.WriteValue<int[]>(version, ref _RedHistogram, rHistogram);
			this.WriteValue<int[]>(version, ref _GreenHistogram, gHistogram);
			this.WriteValue<int[]>(version, ref _BlueHistogram, bHistogram);

			//完成
			return false;
		}
		#endregion
		#region GetHistograms
		unsafe void GetHistograms(IntPtr pixelBufferPtr, int width, int height, int stride, PixelFormat pixelFormat, BitmapPalette palette, int* redHistogramPtr, int* greenHistogramPtr, int* blueHistogramPtr, int* luminanceHistogramPtr)
		{
			if (pixelFormat == PixelFormats.Bgr24)
				this.GetHistogramsFormatBgr24(pixelBufferPtr, width, height, stride, redHistogramPtr, greenHistogramPtr, blueHistogramPtr, luminanceHistogramPtr);
			else if (pixelFormat == PixelFormats.Bgr32 || pixelFormat == PixelFormats.Bgra32)
				this.GetHistogramsFormatBgr32(pixelBufferPtr, width, height, stride, redHistogramPtr, greenHistogramPtr, blueHistogramPtr, luminanceHistogramPtr);
			else if (pixelFormat == PixelFormats.Rgb48)
				this.GetHistogramsFormatRgb48(pixelBufferPtr, width, height, stride, redHistogramPtr, greenHistogramPtr, blueHistogramPtr, luminanceHistogramPtr);
			else
			{
				if (pixelFormat != PixelFormats.Bgr565)
				{
					ImageUtility.EnumerateArgb(pixelBufferPtr, pixelFormat, palette, width, height, stride, (x, y, a, r, g, b) =>
					{
						++redHistogramPtr[r];
						++greenHistogramPtr[g];
						++blueHistogramPtr[b];
						++luminanceHistogramPtr[ColorConversion.ToLuminanceUnsafe(r, g, b)];
						return true;
					});
				}
				else
				{
					ImageUtility.EnumerateArgb(pixelBufferPtr, pixelFormat, palette, width, height, stride, (x, y, a, r, g, b) =>
					{
						++redHistogramPtr[r];
						++greenHistogramPtr[g];
						++blueHistogramPtr[b];
						g >>= 1;
						++luminanceHistogramPtr[ColorConversion.ToLuminanceUnsafe(r, g, b)];
						return true;
					});
				}
			}
		}
		#endregion
		#region GetHistogramsFormatBgr24
		unsafe void GetHistogramsFormatBgr24(IntPtr pixelBufferPtr, int width, int height, int stride, int* redHistogramPtr, int* greenHistogramPtr, int* blueHistogramPtr, int* luminanceHistogramPtr)
		{
			if (stride == (width * 3))
			{
				byte* pixelBuffer = (byte*)pixelBufferPtr;
				for (int i = (width * height); i > 0; --i)
				{
					int b = *(pixelBuffer++);
					int g = *(pixelBuffer++);
					int r = *(pixelBuffer++);
					++redHistogramPtr[r];
					++greenHistogramPtr[g];
					++blueHistogramPtr[b];
					++luminanceHistogramPtr[ColorConversion.ToLuminanceUnsafe(r, g, b)];
				}
			}
			else
			{
				byte* pixelLineBuffer = (byte*)pixelBufferPtr;
				for (int y = height; y > 0; --y, pixelLineBuffer += stride)
				{
					byte* pixelBuffer = (byte*)pixelBufferPtr;
					for (int x = width; x > 0; --x)
					{
						int b = *(pixelBuffer++);
						int g = *(pixelBuffer++);
						int r = *(pixelBuffer++);
						++redHistogramPtr[r];
						++greenHistogramPtr[g];
						++blueHistogramPtr[b];
						++luminanceHistogramPtr[ColorConversion.ToLuminanceUnsafe(r, g, b)];
					}
				}
			}
		}
		#endregion
		#region GetHistogramsFormatBgr32
		unsafe void GetHistogramsFormatBgr32(IntPtr pixelBufferPtr, int width, int height, int stride, int* redHistogramPtr, int* greenHistogramPtr, int* blueHistogramPtr, int* luminanceHistogramPtr)
		{
			if (stride == (width * 4))
			{
				byte* pixelBuffer = (byte*)pixelBufferPtr;
				for (int i = (width * height); i > 0; --i, ++pixelBuffer)
				{
					int b = *(pixelBuffer++);
					int g = *(pixelBuffer++);
					int r = *(pixelBuffer++);
					++redHistogramPtr[r];
					++greenHistogramPtr[g];
					++blueHistogramPtr[b];
					++luminanceHistogramPtr[ColorConversion.ToLuminanceUnsafe(r, g, b)];
				}
			}
			else
			{
				byte* pixelLineBuffer = (byte*)pixelBufferPtr;
				for (int y = height; y > 0; --y, pixelLineBuffer += stride)
				{
					byte* pixelBuffer = (byte*)pixelBufferPtr;
					for (int x = width; x > 0; --x, ++pixelBuffer)
					{
						int b = *(pixelBuffer++);
						int g = *(pixelBuffer++);
						int r = *(pixelBuffer++);
						++redHistogramPtr[r];
						++greenHistogramPtr[g];
						++blueHistogramPtr[b];
						++luminanceHistogramPtr[ColorConversion.ToLuminanceUnsafe(r, g, b)];
					}
				}
			}
		}
		#endregion
		#region GetHistogramsFormatRgb48
		unsafe void GetHistogramsFormatRgb48(IntPtr pixelBufferPtr, int width, int height, int stride, int* redHistogramPtr, int* greenHistogramPtr, int* blueHistogramPtr, int* luminanceHistogramPtr)
		{
			if (stride == (width * 6))
			{
				ushort* pixelBuffer = (ushort*)pixelBufferPtr;
				for (int i = (width * height); i > 0; --i)
				{
					int r = *(pixelBuffer++);
					int g = *(pixelBuffer++);
					int b = *(pixelBuffer++);
					++redHistogramPtr[r];
					++greenHistogramPtr[g];
					++blueHistogramPtr[b];
					++luminanceHistogramPtr[ColorConversion.ToLuminanceUnsafe(r, g, b)];
				}
			}
			else
			{
				byte* pixelLineBuffer = (byte*)pixelBufferPtr;
				for (int y = height; y > 0; --y, pixelLineBuffer += stride)
				{
					ushort* pixelBuffer = (ushort*)pixelLineBuffer;
					for (int x = width; x > 0; --x)
					{
						int r = *(pixelBuffer++);
						int g = *(pixelBuffer++);
						int b = *(pixelBuffer++);
						++redHistogramPtr[r];
						++greenHistogramPtr[g];
						++blueHistogramPtr[b];
						++luminanceHistogramPtr[ColorConversion.ToLuminanceUnsafe(r, g, b)];
					}
				}
			}
		}
		#endregion
		#region GreenHistogram
		/// <summary>
		/// 取得綠色的色階分布。
		/// </summary>
		public int[] GreenHistogram
		{
			get
			{
				return _GreenHistogram;
			}
		}
		#endregion
		#region LuminanceHistogram
		/// <summary>
		/// 取得亮度的階層分布。
		/// </summary>
		public int[] LuminanceHistogram
		{
			get
			{
				return _LuminanceHistogram;
			}
		}
		#endregion
		#region RedHistogram
		/// <summary>
		/// 取得紅色的色階分布。
		/// </summary>
		public int[] RedHistogram
		{
			get
			{
				return _RedHistogram;
			}
		}
		#endregion
		#region SourceImage
		/// <summary>
		/// 取得或設定欲取得色階分布的來源點陣圖。
		/// </summary>
		public IBitmapImage SourceImage
		{
			get
			{
				return _SourceImage;
			}
			set
			{
				this.SetInitializingParameter<IBitmapImage>(ref _SourceImage, value);
			}
		}
		#endregion
		#region ValidateInitParameters
		/// <summary>
		/// 驗證初始化參數是否正確。
		/// </summary>
		/// <returns>True 表示初始化參數正確，False 表示不正確。</returns>
		protected override bool ValidateInitParameters()
		{
			return (base.ValidateInitParameters() && _SourceImage != null);
		}
		#endregion
	}
}
